//auth.service.ts
import { BadRequestException, Inject, Injectable } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { lastValueFrom } from "rxjs";
import { WeChatUserInfo, WeChatError, AccessConfig, AccessTokenInfo } from "./auth.interface";
import { AxiosResponse } from "axios";
import { HttpService } from "@nestjs/axios";
import { Users } from "../users/entities/user.entity";
import { UsersService } from "../users/users.service";
import { ConfigService } from "@nestjs/config";
import { Repository } from "typeorm";
import { compareSync } from "bcryptjs";
import { InjectRepository } from "@nestjs/typeorm";
import { CACHE_MANAGER } from "@nestjs/cache-manager";
import { Cache } from "cache-manager";

@Injectable()
export class AuthService {
	constructor(
		private jwtService: JwtService,
		private usersService: UsersService,
		private httpService: HttpService,
		private readonly configService: ConfigService,
		@InjectRepository(Users)
		private readonly userRepository: Repository<Users>,
		@Inject(CACHE_MANAGER)
		private cacheManager: Cache
	) {}
	private accessTokenInfo: AccessTokenInfo;
	public apiServer = "https://api.weixin.qq.com";

	// 生成token
	createToken(user: Partial<Users>) {
		const access_token_expires_in = Number(this.configService.get("JWT_SHORT_EXPIRES_IN"));
		const refresh_token_expires_in = Number(this.configService.get("JWT_LONG_EXPIRES_IN"));
		const tokens = {
			access_token: this.jwtService.sign(user, {
				secret: this.configService.get("JWT_SHORT_SECRET"),
				expiresIn: access_token_expires_in
			}),
			access_token_expires_in,
			refresh_token: this.jwtService.sign(user, {
				secret: this.configService.get("JWT_LONG_SECRET"),
				expiresIn: refresh_token_expires_in
			}),
			refresh_token_expires_in
		};
		this.cacheManager.set(
			`${this.configService.get("JWT_SHORT_KEY")}:${user.id}`,
			tokens.access_token,
			tokens.access_token_expires_in * 1000 //单位秒转换为毫秒
		);
		this.cacheManager.set(
			`${this.configService.get("JWT_LONG_KEY")}:${user.id}`,
			tokens.refresh_token,
			tokens.refresh_token_expires_in * 1000 //单位秒转换为毫秒
		);
		return tokens;
	}
	async login(account: string, password: string) {
		const user = await this.userRepository
			.createQueryBuilder("users")
			.addSelect("users.password")
			.where("users.account=:account", { account })
			.getOne();

		if (!user) {
			throw new BadRequestException("用户名不正确！");
		}

		if (!compareSync(password, user.password)) {
			throw new BadRequestException("密码错误！");
		}

		return this.createToken({
			id: user.id,
			account: user.account,
			role: user.role
		});
	}
	async refreshToken(user: Partial<Users>) {
		return this.createToken(user);
	}
	async getUser(user: Users) {
		return await this.usersService.findOne(user.id);
		// return true;
	}
	async loginWithWeChat(code) {
		if (!code) {
			throw new BadRequestException("请输入微信授权码");
		}
		await this.getAccessToken(code);

		const user = await this.getUserByOpenid();
		if (!user) {
			// 获取用户信息，注册新用户
			const userInfo: WeChatUserInfo = await this.getUserInfo();
			return this.usersService.registerByWeChat(userInfo);
		}
		return this.createToken(user);
	}

	async getUserInfo() {
		const result: AxiosResponse<WeChatError & WeChatUserInfo> = await lastValueFrom(
			this.httpService.get(
				`${this.apiServer}/sns/userinfo?access_token=${this.accessTokenInfo.accessToken}&openid=${this.accessTokenInfo.openid}`
			)
		);
		if (result.data.errcode) {
			throw new BadRequestException(
				`[getUserInfo] errcode:${result.data.errcode}, errmsg:${result.data.errmsg}`
			);
		}
		console.log("result", result.data);

		return result.data;
	}
	async getUserByOpenid() {
		return await this.usersService.findByOpenid(this.accessTokenInfo.openid);
	}
	async getAccessToken(code) {
		const { APPID, APPSECRET } = process.env;
		if (!APPSECRET) {
			throw new BadRequestException("[getAccessToken]必须有appSecret");
		}
		if (
			!this.accessTokenInfo ||
			(this.accessTokenInfo && this.isExpires(this.accessTokenInfo))
		) {
			// 请求accessToken数据
			const res: AxiosResponse<WeChatError & AccessConfig, any> = await lastValueFrom(
				this.httpService.get(
					`${this.apiServer}/sns/oauth2/access_token?appid=${APPID}&secret=${APPSECRET}&code=${code}&grant_type=authorization_code`
				)
			);

			if (res.data.errcode) {
				throw new BadRequestException(
					`[getAccessToken] errcode:${res.data.errcode}, errmsg:${res.data.errmsg}`
				);
			}
			this.accessTokenInfo = {
				accessToken: res.data.access_token,
				expiresIn: res.data.expires_in,
				getTime: Date.now(),
				openid: res.data.openid
			};
		}

		return this.accessTokenInfo.accessToken;
	}
	isExpires(access) {
		return Date.now() - access.getTime > access.expiresIn * 1000;
	}
}
